
/* License Notice:
**
** This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
** This program is distributed in the hope that it will be useful,
**   but WITHOUT ANY WARRANTY; without even the implied warranty of
**   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
**   GNU General Public License for more details.
** You should have received a copy of the GNU General Public License
**   along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
 * @file state.hpp
 * @author TooOld2Rock'nRoll
 * @date 2024/03/26
 * @brief State interface for the ArcadeFighter library.
 *
 * Implemented following the Game Programming Patterns.
 *
 * @see https://gameprogrammingpatterns.com/state.html
 */
#ifndef _STATE_HPP_
#define _STATE_HPP_

/*---- Includes ----*/
#include <cstring>

#include "input_devices/controller.hpp"


/*---- Class Declaration ----*/
/**
 * @brief Enables the creation of arrays of states of different Characters sub classes!
 * @warning Should <b>not!</b> be used directly outside of this context!!!
 */
class AbstractState
{
    public:
        /** @brief Maps valid state transitions to the current state. */
        struct state_change_s
        {
            controller_button_map_t _input = controller_button_map_t::_button_max; ///<Which key to trigger the state change?
            bool b_state[key_state::_key_state_max] = {false, false, false}; ///<In which states?
            unsigned _next_state = 0; ///<The state to go to (set to max to make it invalid).
            unsigned _command_index = 0; ///The command index to execute (set to max to make it invalid).
        };//end state_change_s


    protected:


    public:
        /** @brief Destructor. */
        virtual ~AbstractState () {};
};//END AbstractState

/**
 * @brief State Interface.
 *
 * Base class for all the character states in the game.
 *
 * @tparam P - the puppet class.
 */
template <typename P>
class State : public AbstractState
{
    public:
        /** @brief Setup the state when changing from other state.  */
        virtual void enter (P &puppet) = 0;

        /**
         * @brief Checks if the state transition is valid.
         * @param puppet - reference to the Character.
         * @param new_state - new state to get to.
         * @return <b>true</b> if new state is valid, <b>false</b> if state change not possible.
         */
        virtual bool checkStateChange (P &puppet, unsigned new_state) const = 0;

        /**
         * @brief Interface to Input Handle Callback.
         * @param puppet - reference to the Character.
         * @param input - which input state has changed.
         * @param input_st - the input new state.
         * @param delta_t - time elapsed since last game loop iteration.
         */
        virtual void handleInput (P &puppet, controller_button_map_t input, key_state input_st, double delta_t) = 0;

        /**
         * @brief Update the Character based on game change in state since last game loop iteration.
         * @param puppet - reference to the Character.
         * @param delta_t - time elapsed since last game loop iteration.
         */
        virtual void update (P &puppet, double delta_t) = 0;
};//END State


/**
 * @brief Automated State Interface.
 * This is the base class to use if you intend on automating state creation with tools or your states have very well
 *    defined and behaved state changes that require no customization.
 *
 * @tparam P - the puppet class.
 * @remarks It´s not elegant to have this much implementation at a header file, but we can´t have templates implemented
 *    in the source files and still pack everything as a static library.
 * @warning Implementation looks and feels (and probably is) a little sketchy without error notification.
 */
template <typename P>
class AutomatedState : public State<P>
{
    protected:
        unsigned u_transition_count = 0; ///<The number of known transitions mapped to handle automatically.
        AbstractState::state_change_s *a_valid_transitions = nullptr; ///<The list of automated transitions.


    public:
        /** @brief Constructor
         * @param transition_count- the number of expected state transitions form this state.
         */
        AutomatedState (unsigned transition_count) : State<P>()
        {
            this->u_transition_count = transition_count;
            if (transition_count) //> 0
                this->a_valid_transitions = new AbstractState::state_change_s[transition_count] ();
        }//End Constructor

        /** @brief Destructor. */
        virtual ~AutomatedState ()
        { if (this->a_valid_transitions) delete []this->a_valid_transitions; }

        /** @brief Adds a state transition to the list.
         * @param index - index in the array.
         * @param state_change - info regarding the transition.
         */
        void addStateTransition (unsigned index, AbstractState::state_change_s &state_change)
        {
            if (this->a_valid_transitions && index < this->u_transition_count)
                std::memcpy (&this->a_valid_transitions[index], &state_change, sizeof (AbstractState::state_change_s));
        }//End addStateTransition ()

        /** @brief Checks if the state transition is valid by searching the transition list for a match.
         * @param puppet - reference to the Character.
         * @param new_state - new state to get to.
         * @return <b>true</b> if new state exists, <b>false</b> if state is not predicted.
         */
        virtual bool checkStateChange (P &puppet, unsigned new_state) const override
        {
            for (int i = this->u_transition_count; i--; )
                if (this->a_valid_transitions[i]._next_state == new_state)
                    return true;
            //end for (int i = this->u_transition_count; i--; )

            return false;
        }//End checkStateChange ()

        /**
         * @brief If input/state are mapped, change puppet state and execute command.
         * @param puppet - reference to the Character.
         * @param input - which input state has changed.
         * @param input_st - the input new state.
         * @param delta_t - time elapsed since last game loop iteration.
         */
        virtual void handleInput (P &puppet, controller_button_map_t input, key_state input_st, double delta_t) override
        {
            for (int i = this->u_transition_count; i--; )
                if (this->a_valid_transitions[i].b_state[input_st])
                    if (puppet.setState (this->a_valid_transitions[i]._next_state)) {
                        puppet.execute (this->a_valid_transitions[i]._command_index, delta_t);
                        break;
                    }
                //end if (this->a_valid_transitions[i].b_state[input_st])
            //end for (int i = this->u_transition_count; i--; )
        }//End handleInput ()
};//END AutomatedState


/** @brief A State that does nothing, useful to fill in behaviors not implemented without triggering errors. */
template <typename P>
class EmptyState : public State<P>
{
    public:
        /** @brief Setup nothing. */
        virtual void enter (P &puppet) override {  }

        /** @brief Does nothing, always returns <b>true</b>. */
        virtual bool checkStateChange (P &puppet, unsigned new_state) const override { return true; }

        /** @brief Does nothing with the input event. */
        virtual void handleInput (P &puppet, controller_button_map_t input, key_state input_st, double delta_t) override {  }

        /** @brief Updates nothing. */
        virtual void update (P &puppet, double delta_t) override {  }
};//END EmptyState

#endif //_STATE_HPP_

